import ast
import tokenize
import token
import re
from StringIO import StringIO


# Returns the leading whitespace of a line of text. Does not modify original
# string.
def leading_whitespace(s):
    w = []
    for c in s:
        if not c.isspace():
            break
        w.append( c )
    return ''.join(w)

# Removes leading whitespace from a list of code lines 'c' which start at line
# number 'start'.  If 'c' is not indented or has mismatched beginning
# indentation (i.e., line 1 begins with '\t' but line 2 begins with '  ' or
# line 2's indentation is less than line 1's) throws an error.
#
# Returns a new list of left-shifted text.  Does not modify original list.
def strip_indentation( c, start=1 ):
    r = []
    indentLevel = None
    multiline = False
    for i,line in enumerate( c ):
        if line.startswith( ('#',) ) or line.isspace():
            #comment line
            r.append('\n')
            continue
        if indentLevel == None:
            indentLevel = leading_whitespace(line)
            if indentLevel == '':
                raise SyntaxError("STRIPS: Expected indented line at %d\n\t'%s'" \
                        % (start+i, line))
        if not line.startswith(indentLevel):
            raise SyntaxError("STRIPS: Expected indented line at %d\n\t'%s'" \
                    % (start+i, line))
        if line[len(indentLevel):].startswith( ('#',) ):
            r.append('\n')
            continue

        if multiline:
            r[-1] += line[len(indentLevel):]
        else:
            r.append(line[len(indentLevel):])
        multiline = (r[-1][-2]=='\\')
        if multiline: r[-1] = r[-1][0:-2]
    return r

def tokenize_code(code):
    g = tokenize.generate_tokens(StringIO(code).readline)
    return [b for (a,b,_,_,_) in g if token.tok_name[a]=='NAME']

def strip_comments(code,prefix='#'):
    """Returns python-style # comments from code"""
    return [line for line in code if len(line)>0 and not line.lstrip().startswith(prefix)]


def split_sections(code,sectionHeadings,start=1):
    if isinstance(code,str):
            code = code.splitlines()
        
    secbegin = {}
    for i,line in enumerate(code):
        line = line.rstrip()
        for sec in sectionHeadings:
            if isinstance(sec,str):
                if sec == line:
                    if line in secbegin:
                        raise RuntimeError('Multiple sections of type '+line)
                    secbegin[line] = i
            elif hasattr(sec,'match'):
                if sec.match(line):
                    if line in secbegin:
                        raise RuntimeError('Multiple sections of type '+line)
                    secbegin[line] = i
            else:
                raise RuntimeError("split_sections takes either strings or RegexObject's as section headings")

    linenos = {}
    sections = {}
    for (k,v) in secbegin.iteritems():
        end = min([len(code)] + [x for x in secbegin.values() if x > v])
        linenos[k] = (v+start,end+start)
        sections[k] = strip_indentation(code[v+1:end],v)
    return linenos,sections


class UnknownNameVisitor(ast.NodeVisitor):
    def __init__(self, env, n=0):
        ast.NodeVisitor.__init__(self)
        self.env = env
        self.max = n
        self.unknownNames = []
        
    def visit_Name(self, node):
        if node.id not in self.env:
            self.unknownNames.append(node.id)
            if self.max != 0 and len(self.unknownNames)>=self.max:
                return False
